const path = require('path')
const fetch = require('node-fetch');


const getCmdArgs = () => {
  const result = {};
  process.argv.forEach((item, index) => {
    if (item.startsWith('--') && process.argv[index + 1] && !process.argv[index + 1].startsWith('-')) {
      result[item.substring(2)] = process.argv[index + 1];
    }
  })
  if (!result.config) {
    result.config = './config';
  }
  return result;
}

const getEndpoint = (api, query = {}) => {
  const params = new URLSearchParams(query);
  if (api.includes('?')) return `${api}&${params.toString()}`;
  return `${api}?${params.toString()}`;
}


const getConfig = () => {
  const { config: configPath } = getCmdArgs();
  if (!configPath) throw new Error('请指定配置文件，可以参看config.example.js');
  const config = require(path.join(process.cwd(), configPath))();
  const result = [];
  config.data.forEach(({ method = 'GET', api, data = {}, headers = {}, responseType, expect }) => {
    const _method = method.toUpperCase();
    const _headers = {
      ...(config.headers || {}),
      ...headers
    };
    const _responseType = responseType || config.responseType;
    let _api = (api.includes('http://') || api.includes('https://')) ? api : `${config.host}${api}`;
    const obj = [
      _api,
      {
        method: _method,
        headers: _headers,
      },
      () => { },
      []
    ]

    if (_method === 'GET') {
      obj[0] = getEndpoint(_api, data)
    } else if (_headers['Content-Type'] === 'application/json') {
      obj[1].body = JSON.stringify(data);
    } else {
      obj[1].body = (new URLSearchParams(data)).toString();
    }

    obj[2] = async (req) => {
      let result;
      if (_responseType === 'json' && req.status == 200) {
        result = await req.json();
      } else {
        result = await req.text();
      }
      return result;
    }

    Object.keys(expect).forEach(key => {
      obj[3].push({ key, value: expect[key] })
    })
    result.push(obj);
  })
  return result;
}


const matchRule = ({ key: rule, value: expect }, doc) => {
  // console.log(111, rule, expect, doc)
  const action = rule.slice(-1);
  const parts = rule.slice(0, -1).split('.');
  let checked = false;
  let temp = parts[0] === "$" ? doc : doc.body;
  parts.forEach((key) => {
    if (key === "$") return;
    if (typeof temp === 'object') {
      temp = temp[key];
    } else if (typeof temp == 'string' && key === 'length') {
      temp = temp[key];
    } else {
      temp = undefined;
    }
  });

  if (action === '=') {
    checked = expect == temp;
  } else if (action == '!') {
    if (typeof temp !== 'undefined') {
      checked = expect !== temp;
    }
  } else if (['^', '$', '*'].includes(action)) {
    if (typeof temp === 'string' || typeof temp === 'number') {
      const _tmp = `${temp}`;
      if (action === '^') {
        checked = _tmp.startsWith(expect)
      } else if (action === '$') {
        checked = _tmp.endsWith(expect)
      } else if (action === '*') {
        checked = _tmp.includes(expect)
      }
    }
  }

  return [checked, temp];
}

const start = async () => {
  const configs = getConfig();
  for (let i = 0; i < configs.length; i++) {
    const [api, option, transformer, rules] = configs[i];
    console.log(`## ${option.method} ${api}`)
    if (option.method !== 'GET') {
      console.log('Request body:')
      console.log('```')
      console.log(option.body);
      console.log('```');
    }
    const req = await fetch(api, option);
    console.log('Response status:', req.status);
    const res = await transformer(req);
    const header = {};
    req.headers.forEach((v, k) => {
      header[k] = v;
    })
    // console.log('```')
    // console.log(typeof res == 'string' ? res : JSON.stringify(res))
    // console.log('```')
    console.log(`|Result|rule|expect|response content|`)
    console.log(`|--|--|--|--|`)
    for (let j = 0; j < rules.length; j++) {
      const [checked, value] = matchRule(rules[j], {
        body: res,
        header,
        status: req.status
      });
      console.log(`|${checked ? '✅' : '❌'}|${rules[j]['key']}|${rules[j].value}|${value}|`)
    }
  }
}

start();